package jvDeformation;

import handler.LineHandler;
import help.DeformationFactory;

import java.awt.image.BufferedImage;
import java.util.Observable;
import java.util.Observer;

import stegru.tools.syncfield.*;
import jv.project.PgGeometryIf;
import jv.project.PvCameraIf;
import jv.project.PvDisplayIf;
import jv.project.PvPickEvent;
import jv.project.PvPickListenerIf;
import jv.vecmath.PdVector;
import jvDeformation.gui.JvDeformationDisplayPanel;
import deformation.Deformation;
import deformation.DeformationGenerator.DeformationType;

public class JvDeformationDisplay implements PvPickListenerIf, Observer{

	public enum HandlerStatus{
		stick, config, remove;	
	}
	//fields
	public final  SyncFields  syncFields;
	public final S_double alpha;
	public final S_Reference<DeformationType> deformationType;
	public final S_Reference<HandlerStatus> handlerStatus;
	public final S_int gridPointsX;
	public final S_int gridPointsY;
	public final S_boolean showGrid;
	//end of fields
	
	final private PvDisplayIf display;
	private JvGrid jvGrid;
	final private JvDeformationHandler jvDeformationHandler;
	private JvDeformationDisplayPanel panel;

	public JvDeformationDisplay(PvDisplayIf display, BufferedImage image, int numGridPointsX, int numGridPointsY) {
		super();
		syncFields = new SyncFields();
		alpha = syncFields.generateDouble(2);
		deformationType = syncFields.generateReference(DeformationType.AffinePointDeformation);
		handlerStatus = syncFields.generateReference(HandlerStatus.config);
		gridPointsX = syncFields.generateInt(numGridPointsX);
		gridPointsY = syncFields.generateInt(numGridPointsY);
		showGrid = syncFields.generateBoolean(false);
		this.display = display;
		this.jvGrid = new JvGrid(numGridPointsX, numGridPointsY, image);
		this.jvDeformationHandler = new JvDeformationHandler();
		
		init();
	}
	public JvDeformationDisplay(PvDisplayIf display, int numGridPointsX, int numGridPointsY) {
		super();
		syncFields = new SyncFields();
		alpha = syncFields.generateDouble(2);
		deformationType = syncFields.generateReference(DeformationType.AffinePointDeformation);
		handlerStatus = syncFields.generateReference(HandlerStatus.config);
		gridPointsX = syncFields.generateInt(numGridPointsX);
		gridPointsY = syncFields.generateInt(numGridPointsY);
		showGrid = syncFields.generateBoolean(false);
		this.display = display;
		JvGridLoader jvgL = new JvGridLoader(display.getFrame(), numGridPointsX, numGridPointsY);
		this.jvGrid = jvgL.loadJvGrid();
		display.setName(display.getName() + " : "+ jvgL.getFile());
		this.jvDeformationHandler = new JvDeformationHandler();
		init();
	}
	
	private JvDeformationDisplay(PvDisplayIf display, JvDeformationDisplay jvDeformationDisplay) {
		super();
		syncFields =jvDeformationDisplay.syncFields;
		alpha = jvDeformationDisplay.alpha;
		deformationType = jvDeformationDisplay.deformationType;
		handlerStatus = jvDeformationDisplay.handlerStatus;
		gridPointsX = jvDeformationDisplay.gridPointsX;
		gridPointsY = jvDeformationDisplay.gridPointsY;
		showGrid = jvDeformationDisplay.showGrid;
		this.display = display;
		this.jvGrid = new JvGrid(jvDeformationDisplay.jvGrid.numGridPointsX, jvDeformationDisplay.jvGrid.numGridPointsY, jvDeformationDisplay.jvGrid.image);
		this.jvDeformationHandler = new JvDeformationHandler();
		this.jvDeformationHandler.sync(jvDeformationDisplay.getJvDeformationHandler());
		jvDeformationDisplay.getJvDeformationHandler().sync(this.jvDeformationHandler);
		init();
	}
	
	public JvDeformationDisplay generateSynctDisplay(PvDisplayIf display){
		return new JvDeformationDisplay(display, this);
	}
	
	@Override
	public void dragDisplay(PvPickEvent pos) {}
	
	@Override
	public void dragInitial(PvPickEvent pos) {}
	
	@Override
	public void dragVertex(PgGeometryIf geom, int index, PdVector vertex) {
	}	
	
	
	public synchronized JvDeformationDisplayPanel getDeformationPanel(){
		if (panel == null) {
			panel = new JvDeformationDisplayPanel(this);
		}
		return panel;
	}
	/**
	 * @return the deformationType
	 */
	public DeformationType getDeformationType() {
		return deformationType.get();
	}
	/**
	 * @return the display
	 */
	public PvDisplayIf getDisplay() {
		return display;
	}
	
	public HandlerStatus getHandlerStatus() {
		return handlerStatus.get();
	}

	/**
	 * @return the jDefoHa
	 */
	public JvDeformationHandler getJvDeformationHandler() {
		return jvDeformationHandler;
	}

	/**
	 * @return the jgrid
	 */
	public JvGrid getJvGrid() {
		return jvGrid;
	}
	
	
	@Override
	public String getName() {
		return this.getClass().getSimpleName();
	}

	final private void init(){
		syncFields.addObserver(this);
		display.removeGeometries();
		display.selectCamera(PvCameraIf.CAMERA_ORTHO_XY);
		display.setAutoCenter(false);
		display.getCamera().setDist(jvGrid.height);
		PdVector center = jvGrid.elementSet.getCenter();
		display.addGeometry(jvGrid);
		display.getCamera().setInterest(new PdVector(center.getEntry(0), center.getEntry(1),0));
		display.addGeometry(jvDeformationHandler);
		display.selectGeometry(jvDeformationHandler);
		jvDeformationHandler.setCenter(center);
		display.setMajorMode(PvDisplayIf.MODE_PICK);
		display.update(jvGrid);
		display.update(jvDeformationHandler);
		display.addPickListener(this);
		jvDeformationHandler.addJvDeformationHandlerListener(jvGrid);
		jvGrid.setDeformationFactory(new DeformationFactory(deformationType.get(), alpha.get()));
	}
	
	public void setJvGrid(JvGrid jvGrid){
		display.removeGeometry(this.jvGrid);
		new ExternJvGrid(display.getViewer().newDisplay(this.jvGrid.getDeformationFactory().getName(), true), this.jvGrid);
		this.jvGrid = jvGrid;
		jvDeformationHandler.addJvDeformationHandlerListener(jvGrid);
		jvGrid.setDeformationFactory(new DeformationFactory(deformationType.get(), alpha.get()));
		PdVector center = jvGrid.elementSet.getCenter();
		display.addGeometry(jvGrid);
		display.getCamera().setInterest(new PdVector(center.getEntry(0), center.getEntry(1),0));
		display.selectGeometry(jvDeformationHandler);
		jvDeformationHandler.setCenter(center);
		jvGrid.elementSet.showEdges(showGrid.get());
		display.update(jvGrid);
		display.update(jvDeformationHandler);
		
	}
	
	
	private void setJvGrid_intern(JvGrid jvGrid){
		display.removeGeometry(this.jvGrid);
		this.jvGrid = jvGrid;
		jvDeformationHandler.addJvDeformationHandlerListener(jvGrid);
		boolean hasFactory =false;
		if (jvGrid.getDeformationFactory() != null) {
			if (jvGrid.getDeformationFactory().alpha == alpha.get() && jvGrid.getDeformationFactory().deformationType ==deformationType.get()) {
				hasFactory =true;
			}
		}
		if (!hasFactory) {
			jvGrid.setDeformationFactory(new DeformationFactory(deformationType.get(), alpha.get()));
		}

		PdVector center = jvGrid.elementSet.getCenter();
		display.addGeometry(jvGrid);
		display.getCamera().setInterest(new PdVector(center.getEntry(0), center.getEntry(1),0));
		display.selectGeometry(jvDeformationHandler);
		jvDeformationHandler.setCenter(center);
		jvGrid.elementSet.showEdges(showGrid.get());
		display.update(jvGrid);
		display.update(jvDeformationHandler);
	}
	
	public void loadnewImage(){
		JvGridLoader jvgL = new JvGridLoader(display.getFrame(), gridPointsX.get(), gridPointsY.get());
		JvGrid loadJvGrid = jvgL.loadJvGrid();
		if (loadJvGrid == null) {
			return;
		}
		setJvGrid(loadJvGrid);
		display.setName(display.getName() + " : "+ jvgL.getFile());
	}
	

	@Override
	public void markVertices(PvPickEvent pos) {	}

	@Override
	public void pickDisplay(PvPickEvent pos) {}

	@Override
	public void pickInitial(PvPickEvent pos) {}

	@Override
	public void pickVertex(PgGeometryIf geom, int index, PdVector vertex) {
		if (geom == jvDeformationHandler) {
			if (handlerStatus.get().compareTo(HandlerStatus.remove) ==0) {
				jvDeformationHandler.removeVertex(index);
			}			
		}
		
		
	}
	
	
	
	public Deformation generateNewDeformation(){
		DeformationFactory df =new DeformationFactory(deformationType.get(), alpha.get());
		return df.generate(jvDeformationHandler.generateHandler());
	}
	
	public Deformation generateNewDeformation(LineHandler linehandler){
		DeformationFactory df =new DeformationFactory(deformationType.get(), alpha.get());
		return df.generate(linehandler);
	}

	@Override
	public void selectGeometry(PgGeometryIf geom) {}

	

	public void stick() {
		jvDeformationHandler.stickPointsOnImage();
		display.update(jvGrid);
	}

	@Override
	public void unmarkVertices(PvPickEvent pos) {}

	public void unstick() {
		jvDeformationHandler.releasePoints();
	}
	
	@Override
	public void update(Observable o, Object var) {
		if (o == syncFields) {
			if (var == alpha) {
				jvGrid.setDeformationFactory(new DeformationFactory(deformationType.get(), alpha.get()));
				display.update(jvGrid);
			}else if (var == deformationType) {
				if (this.handlerStatus.get() == HandlerStatus.stick) {
					jvGrid.setDeformationFactory(new DeformationFactory(deformationType.get(), alpha.get()));
					display.update(jvGrid);
				}
			}else if(var == handlerStatus){
				if (handlerStatus.get() != HandlerStatus.stick) {
					unstick();
				}else if (handlerStatus.get() == HandlerStatus.stick) {
					stick();
				}
				jvGrid.setDeformationFactory(new DeformationFactory(deformationType.get(), alpha.get()));
			}else if (var == gridPointsX || var == gridPointsY) {
				setJvGrid_intern(jvGrid.newInstance(gridPointsX.get(), gridPointsY.get()));
			}else if (var == showGrid) {
				jvGrid.elementSet.showEdges(showGrid.get());
				jvGrid.repaint();
			}
		}
		
	}



}
